package com.onesports.intelligent.k12.polarlight.service.course;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.onesports.framework.kit.common.util.BeanUtils;
import com.onesports.framework.kit.common.util.ParamUtils;
import com.onesports.framework.kit.common.util.TimeUtils;
import com.onesports.intelligent.k12.polarlight.common.domain.entity.BaseEntity;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.BusinessException;
import com.onesports.intelligent.k12.polarlight.common.utils.Assert;
import com.onesports.intelligent.k12.polarlight.common.utils.DateUtils;
import com.onesports.intelligent.k12.polarlight.domain.dto.common.IdDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseOpenDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseScheduleDTO;
import com.onesports.intelligent.k12.polarlight.domain.dto.course.CourseScheduleQueryDTO;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.Course;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.CourseSchedule;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.CourseScheduleDetail;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.CourseScheduleDetailCoachRel;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.Product;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.ProductCampusRel;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.ProductSellPriceRel;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.Coach;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.OrganizationCampus;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.OrganizationStaff;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CoachScheduleUseSituationVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseDateNumVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseDateVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseOpenVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseScheduleDetailVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseScheduleVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.CourseSellInfo;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.PreScheduleVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.SaveScheduleVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.course.SpellClassInfoVO;
import com.onesports.intelligent.k12.polarlight.domain.vo.organization.CoachServeVO;
import com.onesports.intelligent.k12.polarlight.enums.ClassInfoStatusEnum;
import com.onesports.intelligent.k12.polarlight.enums.ScheduleDetailStatusEnum;
import com.onesports.intelligent.k12.polarlight.enums.ScheduleTypeEnum;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseScheduleDetailCoachRelMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseScheduleDetailMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseScheduleMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseSellPriceRelMapper;
import com.onesports.intelligent.k12.polarlight.mapper.market.ProductCampusRelMapper;
import com.onesports.intelligent.k12.polarlight.mapper.market.ProductMapper;
import com.onesports.intelligent.k12.polarlight.mapper.market.ProductSellPriceRelMapper;
import com.onesports.intelligent.k12.polarlight.mapper.organization.OrganizationCampusMapper;
import com.onesports.intelligent.k12.polarlight.service.coach.CoachManager;
import com.onesports.intelligent.k12.polarlight.service.course.manage.CourseOpenService;
import com.onesports.intelligent.k12.polarlight.service.grade.ClassInfoService;
import com.onesports.intelligent.k12.polarlight.service.organization.OrganizationStaffService;

import lombok.RequiredArgsConstructor;

/**
 * @author Wsy
 **/
@Service
@RequiredArgsConstructor
public class CourseScheduleService extends ServiceImpl<CourseScheduleMapper, CourseSchedule> {

    private final CourseScheduleMapper courseScheduleMapper;
    private final ScheduleDetailCoachRelService scheduleDetailCoachRelService;
    private final CourseScheduleDetailManager courseScheduleDetailManager;
    private final CourseOpenService courseOpenService;
    private final CoachManager coachManager;
    private final CourseManger courseManger;
    private final OrganizationCampusMapper organizationCampusMapper;
    private final OrganizationStaffService organizationStaffService;
    private final ClassInfoService classInfoService;
    private final CourseScheduleDetailCoachRelMapper courseScheduleDetailCoachRelMapper;
    private final ProductMapper productMapper;
    private final ProductCampusRelMapper productCampusRelMapper;
    private final CourseSellPriceRelMapper courseSellPriceRelMapper;
    private final ProductSellPriceRelMapper productSellPriceRelMapper;
    private final CourseScheduleDetailMapper courseScheduleDetailMapper;

    /**
     * 课表预览
     *
     * @param dto 入参
     * @return List<PreScheduleVO>
     */
    public List<PreScheduleVO> preview(CourseScheduleDTO dto) {
        //1、获取两个日期之间的所有日期
        List<Date> dates = DateUtils.allDaysBetweenTwoDate(dto.getStartDate(), dto.getEndDate());

        //2、根据课程id和机构id获取课程开设时间
        List<CourseOpenVO> courseOpenList = this.courseOpenService.listByCourseIdAndOrganizationId(dto.getCourseId(), dto.getOrganizationId());
        if (ParamUtils.isEmpty(courseOpenList)) {
            return new ArrayList<>();
        }
        List<String> scheduleTemplateIds = courseOpenList.stream().map(CourseOpenVO::getScheduleTemplateId).collect(Collectors.toList());
        //3、根据课程id、校区id、课程开设时间获取符合条件的教练信息（信息包括：教练id，教练名称，教练服务时段id，教练服务课程id）
        List<CoachServeVO> coachServeList = this.coachManager.serveInfo(dto.getCourseId(), dto.getCampusId(), scheduleTemplateIds,
                Arrays.asList(ClassInfoStatusEnum.IN_CLASS.getValue(), ClassInfoStatusEnum.FINISH_CLASS.getValue(), ClassInfoStatusEnum.OPEN_CLASS.getValue()));
        return this.previewCommon(coachServeList, dto.getCampusId(), dto.getCourseType(), dto.getWeekCode(), dates, courseOpenList, dto.getDates());
    }

    private List<PreScheduleVO> previewCommon(List<CoachServeVO> coachServeList, String campusId, String courseType,
                                              String[] weekCode, List<Date> dates, List<CourseOpenVO> courseOpenList, List<String> choiceDates) {
        if (ParamUtils.isNotEmpty(coachServeList)) {
            Map<String, List<CoachServeVO>> coachMap = coachServeList.stream()
                    .filter(f -> ParamUtils.isNotEmpty(f.getScheduleTemplateId()))
                    .collect(Collectors.groupingBy(CoachServeVO::getScheduleTemplateId));
            //符合条件的数据合并处理
            courseOpenList.forEach(i -> {
                List<CoachServeVO> coachList = coachMap.get(i.getScheduleTemplateId());
                if (ParamUtils.isNotEmpty(coachList)) {
                    List<String> coachIds = coachList.stream().map(CoachServeVO::getCoachId).collect(Collectors.toList());
                    List<String> coachNames = coachList.stream().map(CoachServeVO::getCoachName).collect(Collectors.toList());
                    i.setCoachIds(coachIds);
                    i.setCoachNames(coachNames);
                }
            });
        }
        //4、根据开始结束日期填充第2、3步数据
        //获取课程信息
        Map<String, String> courseMap = Optional.ofNullable(this.courseManger.list())
                .orElse(new ArrayList<>())
                .stream().collect(Collectors.toMap(BaseEntity::getId, Course::getCourseName));

        //获取课程信息
        Map<String, String> campusMap = Optional.ofNullable(this.organizationCampusMapper.selectList(null))
                .orElse(new ArrayList<>())
                .stream().collect(Collectors.toMap(BaseEntity::getId, OrganizationCampus::getCampusName));

        //获取可用的教练（未被购买的）
        List<CoachScheduleUseSituationVO> useCoach = this.courseScheduleDetailCoachRelMapper.selectCanUseCoach();

        //获取校区信息
        List<PreScheduleVO> list = new ArrayList<>();
        if (ParamUtils.isEmpty(useCoach)) {
            for (Date i : dates) {
                for (CourseOpenVO c : courseOpenList) {
                    PreScheduleVO schedule = BeanUtils.copyProperties(c, PreScheduleVO.class);
                    list.add(schedule.setCourseName(courseMap.get(c.getCourseId()))
                            .setCourseDate(i)
                            .setCourseOpenId(c.getId())
                            .setCampusName(campusMap.get(campusId))
                            .setWeekCode(DateUtils.dateToWeekCode(i)));
                }
            }
        } else {
            for (Date i : dates) {
                for (CourseOpenVO c : courseOpenList) {
                    PreScheduleVO schedule = BeanUtils.copyProperties(c, PreScheduleVO.class);
                    if (ParamUtils.isNotEmpty(c.getCoachIds())) {
                        List<String> coachIdsTemp = new ArrayList<>(c.getCoachIds());
                        List<String> coachNamesTemp = new ArrayList<>(c.getCoachNames());
                        String date = TimeUtils.toText(i, TimeUtils.DATE_FORMAT);
                        String startTime = TimeUtils.toText(c.getStartTime(), TimeUtils.TIME_FORMAT);
                        String endTime = TimeUtils.toText(c.getEndTime(), TimeUtils.TIME_FORMAT);
                        //获取当前日期+时段不可用的教练
                        List<CoachScheduleUseSituationVO> filterData = useCoach.stream()
                                .filter(f -> f.getStatus().equals(false)
                                        && (TimeUtils.toText(f.getCourseDate(), TimeUtils.DATE_FORMAT)).equals(date)
                                        && (TimeUtils.toText(f.getStartTime(), TimeUtils.TIME_FORMAT)).equals(startTime)
                                        && (TimeUtils.toText(f.getEndTime(), TimeUtils.TIME_FORMAT)).equals(endTime))
                                .collect(Collectors.toList());
                        if (ParamUtils.isNotEmpty(filterData)) {
                            //不可用教练
                            List<String> coachIds = filterData.stream().map(CoachScheduleUseSituationVO::getCoachId).collect(Collectors.toList());
                            List<String> coachNames = filterData.stream().map(CoachScheduleUseSituationVO::getCoachName).collect(Collectors.toList());
                            coachIdsTemp.removeAll(coachIds);
                            coachNamesTemp.removeAll(coachNames);
                            schedule.setCoachIds(coachIdsTemp);
                            schedule.setCoachNames(coachNamesTemp);
                        }
                    }
                    list.add(schedule.setCourseName(courseMap.get(c.getCourseId()))
                            .setCourseDate(i)
                            .setCourseOpenId(c.getId())
                            .setCampusName(campusMap.get(campusId))
                            .setWeekCode(DateUtils.dateToWeekCode(i)));
                }
            }
        }


        //按星期排课则过滤出周期内选中的星期
        if (courseType.equals(ScheduleTypeEnum.WEEK.getValue())) {
            if (ParamUtils.isNotEmpty(weekCode)) {
                List<String> weekCodes = Arrays.asList(weekCode);
                list = list.stream().filter(i -> weekCodes.contains(i.getWeekCode())).collect(Collectors.toList());
            }

        } else {
            if (ParamUtils.isNotEmpty(choiceDates)) {
                list = list.stream().filter(i -> choiceDates.contains(TimeUtils.toText(i.getCourseDate(), TimeUtils.DATE_FORMAT))).collect(Collectors.toList());
            }
        }
        return list;
    }

    /**
     * 添加课表
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public SaveScheduleVO add(CourseScheduleDTO dto) {
        Date date = new Date();
        //校验参数是否合理
        this.checkParam(dto, date);
        //校验课表名称是否存在
        this.checkScheduleName(dto, null);
        //校验课表日期是否合法
        this.checkCourseDate(dto.getStartDate(), dto.getEndDate());
        //校验当前课程是否设置了开设时间以及是否有关联商品
        //返回参数
        SaveScheduleVO saveSchedule = new SaveScheduleVO();
        this.checkCourse(dto, saveSchedule);
        return saveSchedule;
    }

    private void checkParam(CourseScheduleDTO dto, Date date) {
        Date current = TimeUtils.toDate(TimeUtils.toText(date, TimeUtils.DATE_FORMAT), TimeUtils.DATE_FORMAT);
        //开始日期必须大于等于当前日期
        Assert.isFalse(dto.getStartDate().getTime() < current.getTime(), "上课开始日期必须大于等于当前日期!");
        //如果是按日期排课且选择的日期小于开始日期或者大于结束日期都不行
        if (dto.getCourseType().equals(ScheduleTypeEnum.DATE.getValue())) {
            for (String dtoDate : dto.getDates()) {
                long time = TimeUtils.toDate(dtoDate, TimeUtils.DATE_FORMAT).getTime();
                Assert.isFalse(time < dto.getStartDate().getTime() || time > dto.getEndDate().getTime(), "当前选中日期：{" + dtoDate + "}不在上课日期范围内，请重新选择!");
            }
        }

    }


    /**
     * 校验当前课程是否设置了开设时间
     *
     * @param dto          入参
     * @param saveSchedule 返回参数
     */
    private void checkCourse(CourseScheduleDTO dto, SaveScheduleVO saveSchedule) {
        //查询开设时间
        List<CourseOpenVO> courseOpens = this.courseOpenService.listByCourseIdAndOrganizationId(dto.getCourseId(), dto.getOrganizationId());
        if (ParamUtils.isEmpty(courseOpens)) {
            saveSchedule.setIsOpen(true);
        }


        Product product = this.getProductCourseRel(dto);

        if (product == null) {
            saveSchedule.setIsCourseProduct(true);
        }


        //两个条件都满足直接保存
        if (saveSchedule.getIsCourseProduct().equals(Boolean.FALSE) && saveSchedule.getIsOpen().equals(Boolean.FALSE)) {
            this.toSave(dto);
        }
    }

    private Product getProductCourseRel(CourseScheduleDTO dto) {
        return this.productMapper.selectOne(new LambdaQueryWrapper<Product>()
                .eq(Product::getCourseId, dto.getCourseId())
                .eq(Product::getOrganizationId, dto.getOrganizationId()));
    }


    /**
     * 确认保存
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public void toSave(CourseScheduleDTO dto) {
        //课表预览
        List<PreScheduleVO> preview = this.preview(dto);
        if (dto.getCourseType().equals(ScheduleTypeEnum.WEEK.getValue()) && ParamUtils.isEmpty(preview)) {
            throw new BusinessException("所选星期不在上课日期范围内，请重新选择");
        }
        //courseSchedule
        CourseSchedule schedule = this.saveCourseSchedule(dto, preview.size());

        if (ParamUtils.isNotEmpty(dto.getId())) {
            //删除原详情和详情教练信息
            this.deleteOldData(schedule.getId());
        }
        //courseScheduleDetail 详情状态为未拼班
        this.saveCourseScheduleDetail(schedule.getId(), preview, dto.getOrganizationId(), schedule.getCampusId());

        //如若课程绑定了商品则维护该商品的校区关系
        this.bindCourseProduct(dto);

    }

    /**
     * 如若课程绑定了商品则维护该商品的校区关系
     *
     * @param dto 入参
     */
    private void bindCourseProduct(CourseScheduleDTO dto) {

        //获取课程在当前机构绑定的商品信息
        Product product = this.getProductCourseRel(dto);
        if (product != null) {
            //校验是否已绑定了校区
            ProductCampusRel productCampusRel = this.productCampusRelMapper.selectOne(new LambdaQueryWrapper<ProductCampusRel>()
                    .eq(ProductCampusRel::getCampusId, dto.getCampusId())
                    .eq(ProductCampusRel::getProductId, product.getId()));

            if (productCampusRel == null) {
                productCampusRel = new ProductCampusRel();
                productCampusRel.setProductId(product.getId());
                productCampusRel.setCampusId(dto.getCampusId());
                productCampusRel.setOrganizationId(product.getOrganizationId());
                this.productCampusRelMapper.insert(productCampusRel);
            }
            //获取课程售价信息
            List<CourseSellInfo> courseSellInfos = this.courseSellPriceRelMapper.courseSellInfo(dto.getCourseId(), dto.getCampusId());
            if (ParamUtils.isNotEmpty(courseSellInfos)) {
                List<String> sellPriceRelIds = courseSellInfos.stream().map(CourseSellInfo::getCourseSellPriceRelId).collect(Collectors.toList());

                //校验是否存在商品售价
                List<ProductSellPriceRel> productSellPriceRelList = this.productSellPriceRelMapper.selectList(new LambdaQueryWrapper<ProductSellPriceRel>()
                        .eq(ProductSellPriceRel::getProductId, product.getId())
                        .eq(ProductSellPriceRel::getProductCampusId, productCampusRel.getId())
                        .in(ProductSellPriceRel::getCourseSellPriceRelId, sellPriceRelIds));

                if (ParamUtils.isNotEmpty(productSellPriceRelList)) {
                    Map<String, List<ProductSellPriceRel>> sellPriceMap = productSellPriceRelList.stream()
                            .collect(Collectors.groupingBy(ProductSellPriceRel::getCourseSellPriceRelId));

                    for (CourseSellInfo courseSellInfo : courseSellInfos) {
                        List<ProductSellPriceRel> relList = sellPriceMap.get(courseSellInfo.getCourseSellPriceRelId());
                        if (ParamUtils.isEmpty(relList)) {
                            ProductSellPriceRel rel = new ProductSellPriceRel();
                            rel.setProductId(product.getId())
                                    .setProductCampusId(productCampusRel.getId())
                                    .setTypeName(courseSellInfo.getTypeName())
                                    .setMaxNum(courseSellInfo.getMaxNum())
                                    .setMinNum(courseSellInfo.getMinNum())
                                    .setPrice(courseSellInfo.getSellPrice())
                                    .setCourseSellPriceRelId(courseSellInfo.getCourseSellPriceRelId());
                            this.productSellPriceRelMapper.insert(rel);
                        }
                    }

                } else {
                    for (CourseSellInfo courseSellInfo : courseSellInfos) {
                        ProductSellPriceRel rel = new ProductSellPriceRel();
                        rel.setProductId(product.getId())
                                .setProductCampusId(productCampusRel.getId())
                                .setTypeName(courseSellInfo.getTypeName())
                                .setMaxNum(courseSellInfo.getMaxNum())
                                .setMinNum(courseSellInfo.getMinNum())
                                .setPrice(courseSellInfo.getSellPrice())
                                .setCourseSellPriceRelId(courseSellInfo.getCourseSellPriceRelId());
                        this.productSellPriceRelMapper.insert(rel);
                    }
                }
            }
        }
    }

    private void checkCourseDate(Date startDate, Date endDate) {
        if (startDate.after(endDate)) {
            throw new BusinessException("开始日期必须在结束日期之前");
        }
    }

    /**
     * 校验课表名称是否存在
     *
     * @param dto        入参
     * @param scheduleId 课表id
     */
    private void checkScheduleName(CourseScheduleDTO dto, String scheduleId) {
        List<CourseSchedule> list = this.list(new LambdaQueryWrapper<CourseSchedule>()
                .eq(CourseSchedule::getScheduleName, dto.getScheduleName())
                .ne(ParamUtils.isNotEmpty(scheduleId), BaseEntity::getId, scheduleId));

        if (ParamUtils.isNotEmpty(list)) {
            throw new BusinessException("课表名称已存在，请重新输入");
        }
    }

    /**
     * 保存课表详情
     *
     * @param scheduleId 课表id
     * @param preview    详情信息
     */
    private void saveCourseScheduleDetail(String scheduleId, List<PreScheduleVO> preview, String organizationId, String campusId) {
        List<CourseScheduleDetailCoachRel> coachList = new ArrayList<>();
        preview.forEach(i -> {
            //课表详情
            CourseScheduleDetail courseScheduleDetail = BeanUtils.copyProperties(i, CourseScheduleDetail.class);
            courseScheduleDetail.setScheduleId(scheduleId);
            courseScheduleDetail.setOrganizationId(organizationId);
            courseScheduleDetail.setCampusId(campusId);
            this.courseScheduleDetailManager.save(courseScheduleDetail);

            //课表详情教练关联数据
            List<String> coachIds = i.getCoachIds();
            if (ParamUtils.isNotEmpty(coachIds)) {
                coachList.addAll(coachIds.stream().map(c -> {
                    CourseScheduleDetailCoachRel rel = new CourseScheduleDetailCoachRel();
                    return rel.setScheduleDetailId(courseScheduleDetail.getId()).setCoachId(c);
                }).collect(Collectors.toList()));
            }

        });

        if (ParamUtils.isNotEmpty(coachList)) {
            this.scheduleDetailCoachRelService.saveBatch(coachList);
        }

    }


    /**
     * 保存课表
     *
     * @param dto 入参
     * @return CourseSchedule
     */
    private CourseSchedule saveCourseSchedule(CourseScheduleDTO dto, Integer totalNum) {
        CourseSchedule schedule = BeanUtils.copyProperties(dto, CourseSchedule.class);
        schedule.setTotalNum(totalNum);
        this.saveOrUpdate(schedule);
        return schedule;
    }

    /**
     * 编辑课表
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public SaveScheduleVO edit(CourseScheduleDTO dto) {
        SaveScheduleVO saveSchedule = new SaveScheduleVO();
        //校验课表名称是否存在
        this.checkScheduleName(dto, dto.getId());
        //查一遍课表 校验课表状态是否发布，已发布不可编辑
        CourseSchedule courseSchedule = this.getById(dto.getId());
        if (Boolean.TRUE.equals(courseSchedule.getPublishStatus())) {
            throw new BusinessException("已发布的课表不允许编辑");
        }
        this.checkCourse(dto, saveSchedule);
        return saveSchedule;
    }

    /**
     * 删除原详情和详情教练信息
     *
     * @param scheduleId 课表id
     */
    private void deleteOldData(String scheduleId) {
        List<CourseScheduleDetail> details = this.courseScheduleDetailManager.listByScheduleIds(Collections.singletonList(scheduleId));
        List<String> detailIds = details.stream().map(BaseEntity::getId).collect(Collectors.toList());
        this.courseScheduleDetailManager.removeByIds(detailIds);
        this.scheduleDetailCoachRelService.removeByScheduleDetailIds(detailIds);
    }


    /**
     * 分页查询
     *
     * @param page 分页参数
     * @param dto  入参
     * @return IPage<CourseScheduleVO>
     */
    public IPage<CourseScheduleVO> schedulePage(Page<CourseScheduleVO> page, CourseScheduleQueryDTO dto) {
        IPage<CourseScheduleVO> iPage = this.courseScheduleMapper.schedulePage(page, dto);
        List<CourseScheduleVO> records = iPage.getRecords();
        if (ParamUtils.isNotEmpty(records)) {
            List<String> scheduleIds = records.stream().map(CourseScheduleVO::getId).collect(Collectors.toList());
            Map<String, List<CourseScheduleDetail>> detailMap = this.courseScheduleDetailManager.listByScheduleIds(scheduleIds).stream()
                    .collect(Collectors.groupingBy(CourseScheduleDetail::getScheduleId));
            if (ParamUtils.isNotEmpty(detailMap)) {
                records.forEach(i -> {
                    i.setCourseDate(detailMap.get(i.getId()).stream()
                            .sorted(Comparator.comparing(CourseScheduleDetail::getCourseDate))
                            .map(c -> TimeUtils.toText(c.getCourseDate(), TimeUtils.DATE_FORMAT))
                            .distinct()
                            .collect(Collectors.toList()));
                    String[] weekCode = i.getWeekCode();
                    if (ParamUtils.isNotEmpty(weekCode)) {
                        List<String> weeks = Arrays.asList(weekCode);
                        String[] strings = weeks.stream()
                                .map(Integer::valueOf)
                                .sorted(Comparator.comparing(Integer::new))
                                .collect(Collectors.toList())
                                .stream().map(String::valueOf).toArray(String[]::new);
                        i.setWeekCode(strings);

                    }

                });
            }
        }
        return iPage;
    }


    /**
     * 删除
     *
     * @param dto 入参
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(IdDTO dto) {
        CourseSchedule courseSchedule = this.getById(dto.getId());
        if (courseSchedule.getPublishStatus().equals(Boolean.TRUE)) {
            throw new BusinessException("该课表为发布状态不可删除");
        }
        //删除课表
        this.removeById(dto.getId());
        //删除课表详情
        this.deleteOldData(dto.getId());
    }


    /**
     * 预览课表
     * 说明：
     * 该接口适用于运营端-校区管理、教练管理的查看排课表以及机构端排课表管理的预览功能
     * 运营端：
     * 校区管理调用时以下参数"必传"： campusId、startDate、endDate；其他参数依据业务情况传参，没有的情况下传null
     * 教练管理调用时以下参数"必传"： startDate、endDate、coachId;其他同上
     * 机构端：
     * 排课表管理调用时以下参数"必传"： scheduleId
     *
     * @param scheduleId 课表id
     * @param campusIds  校区ids
     * @param courseId   课程id
     * @param startDate  课表开始日期
     * @param endDate    课表结束日期
     * @param coachId    教练id
     * @return List<CourseScheduleDetailVO>
     */
    public List<CourseScheduleDetailVO> schedulePreview(String scheduleId, List<String> campusIds, String courseId, String startDate, String endDate, String coachId) {
        //根据条件获取所有时段
        List<CourseScheduleDetailVO> details = this.courseScheduleDetailManager.schedulePreview(scheduleId, campusIds, courseId, startDate, endDate, coachId);
        if (ParamUtils.isEmpty(details)) {
            return new ArrayList<>();
        }
        List<String> scheduleDetailIds = details.stream().map(CourseScheduleDetailVO::getId).collect(Collectors.toList());
        //获取时段教练关联表
        List<CourseScheduleDetailCoachRel> coachRelList = this.scheduleDetailCoachRelService.getByScheduleDetailIds(scheduleDetailIds);

        Map<String, String> coachMap = this.coachManager.list().stream().collect(Collectors.toMap(BaseEntity::getId, Coach::getOrganizationStaffId));
        Map<String, String> staffMap = this.organizationStaffService.list().stream().collect(Collectors.toMap(BaseEntity::getId, OrganizationStaff::getStaffName));
        if (ParamUtils.isNotEmpty(coachRelList)) {
            Map<String, List<CourseScheduleDetailCoachRel>> coachRelMap = coachRelList.stream().collect(Collectors.groupingBy(CourseScheduleDetailCoachRel::getScheduleDetailId));
            details.forEach(i -> {
                List<CourseScheduleDetailCoachRel> coach = coachRelMap.get(i.getId());
                if (ParamUtils.isNotEmpty(coach)) {
                    List<String> coachIds = coach.stream().map(CourseScheduleDetailCoachRel::getCoachId).distinct().collect(Collectors.toList());
                    i.setCoachIds(coachIds);
                    List<String> coachNames = new ArrayList<>();
                    coachIds.forEach(c -> {
                        String staffId = coachMap.get(c);
                        String coachName = ParamUtils.isNotEmpty(staffId) ? staffMap.get(staffId) : "";
                        if (ParamUtils.isNotEmpty(coachName)) {
                            coachNames.add(coachName);
                        }
                    });
                    i.setCoachName(coachNames);
                }
            });
        }
        //获取时段拼班信息
        List<SpellClassInfoVO> infos = this.classInfoService.spellClassInfo(scheduleDetailIds, coachId);
        if (ParamUtils.isNotEmpty(infos)) {
            Map<String, List<SpellClassInfoVO>> infoMap = infos.stream()
                    .filter(f -> ParamUtils.isNotEmpty(f.getScheduleDetailId()))
                    .collect(Collectors.groupingBy(SpellClassInfoVO::getScheduleDetailId));
            details.forEach(i -> i.setInfos(infoMap.get(i.getId())));
        }

        return details;
    }


    /**
     * 发布课表
     *
     * @param dto 入参
     */
    public void publish(IdDTO dto) {
        this.update(new UpdateWrapper<CourseSchedule>()
                .eq("id", dto.getId())
                .set("publish_status", true));
    }


    /**
     * 查看校区未来一周的课表
     *
     * @param campusId 校区id
     * @param courseId 课程id
     * @return List<CourseScheduleDetailVO>
     */
    public List<CourseScheduleDetailVO> futureSchedule(String campusId, String courseId) {
        Date startDate = new Date();
        Date endDate = TimeUtils.addDay(startDate, 6);
        return this.schedulePreview(null, ParamUtils.isEmpty(campusId) ? new ArrayList<>() : Collections.singletonList(campusId), courseId, TimeUtils.toText(startDate, TimeUtils.DATE_FORMAT), TimeUtils.toText(endDate, TimeUtils.DATE_FORMAT), null);
    }


    /**
     * 更新未发布的课表时段数据
     *
     * @param dto 入参
     */
    public void updUnPublishCourseSchedule(CourseOpenDTO dto, List<CourseOpenVO> courseOpenList) {
        List<CourseSchedule> courseSchedules = this.courseScheduleMapper.selectList(new LambdaQueryWrapper<CourseSchedule>()
                .eq(CourseSchedule::getCourseId, dto.getCourseId())
                .eq(CourseSchedule::getOrganizationId, dto.getOrganizationId())
                .eq(CourseSchedule::getPublishStatus, false));

        if (ParamUtils.isNotEmpty(courseSchedules)) {
            //删除课表原时段和教练关联表数据
            List<String> scheduleIds = courseSchedules.stream().map(BaseEntity::getId).collect(Collectors.toList());
            List<CourseScheduleDetail> details = this.courseScheduleDetailManager.listByScheduleIds(scheduleIds);
            List<String> scheduleDetailIds = details.stream().map(BaseEntity::getId).collect(Collectors.toList());
            this.courseScheduleDetailManager.removeByIds(scheduleDetailIds);
            this.scheduleDetailCoachRelService.removeByScheduleDetailIds(scheduleDetailIds);

            List<String> scheduleTemplateIds = courseOpenList.stream().map(CourseOpenVO::getId).collect(Collectors.toList());
            //3、获取符合条件的教练信息（信息包括：教练id，教练名称，教练服务时段id，教练服务课程id,校区id）
            List<CoachServeVO> coachServeList = this.coachManager.serveInfo(dto.getCourseId(), null, scheduleTemplateIds,
                    Arrays.asList(ClassInfoStatusEnum.IN_CLASS.getValue(), ClassInfoStatusEnum.FINISH_CLASS.getValue(), ClassInfoStatusEnum.OPEN_CLASS.getValue()));
            for (CourseSchedule courseSchedule : courseSchedules) {
                this.resetScheduleDetail(courseSchedule, courseOpenList, coachServeList, dto.getOrganizationId());
            }

        }
    }

    /**
     * 重置课表时段
     *
     * @param courseSchedule 课表
     * @param list           新的开设时间
     * @param coachServeList 家里可服务时段信息
     */
    private void resetScheduleDetail(CourseSchedule courseSchedule, List<CourseOpenVO> list, List<CoachServeVO> coachServeList, String organizationId) {
        //获取课表时间范围的所有日期
        List<Date> dates = DateUtils.allDaysBetweenTwoDate(courseSchedule.getStartDate(), courseSchedule.getEndDate());
        if (ParamUtils.isNotEmpty(coachServeList)) {
            List<CourseScheduleDetail> details = this.courseScheduleDetailManager.listByScheduleIds(Collections.singletonList(courseSchedule.getId()));
            List<String> choiceDates = details.stream().map(i -> TimeUtils.toText(i.getCourseDate(), TimeUtils.DATE_FORMAT)).distinct().collect(Collectors.toList());
            List<CoachServeVO> serveList = coachServeList.stream().filter(f -> f.getCampusId().equals(courseSchedule.getCampusId())).collect(Collectors.toList());
            List<PreScheduleVO> preview = this.previewCommon(serveList, courseSchedule.getCampusId(), courseSchedule.getCourseType(), courseSchedule.getWeekCode(), dates,
                    list, choiceDates);
            //删除原详情和详情教练信息
            this.deleteOldData(courseSchedule.getId());
            this.saveCourseScheduleDetail(courseSchedule.getId(), preview, organizationId, courseSchedule.getCampusId());
        }

    }

    public CourseDateNumVO getCourseDate(String scheduleId,String startTime,String endTime) {

        CourseDateNumVO vo = new CourseDateNumVO();
        List<CourseDateVO> courseDateVOList = Lists.newArrayList();
        LambdaQueryWrapper<CourseScheduleDetail> csdWrap = new LambdaQueryWrapper<CourseScheduleDetail>();
        csdWrap.eq(CourseScheduleDetail::getScheduleId,scheduleId);
        csdWrap.ne(CourseScheduleDetail::getStatus, ScheduleDetailStatusEnum.CANCEL.getValue());
        List<CourseScheduleDetail> list = courseScheduleDetailMapper.selectList(csdWrap);
        if(ParamUtils.isNotEmpty(list)){
            List<CourseScheduleDetail> courseScheduleDetailList =  list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(
                CourseScheduleDetail::getCourseDate))), ArrayList::new));
            for (CourseScheduleDetail courseScheduleDetail : courseScheduleDetailList) {
                CourseDateVO courseDateVO = new CourseDateVO();
                courseDateVO.setCourseDate(courseScheduleDetail.getCourseDate());
                courseDateVOList.add(courseDateVO);
            }
        }
        //根据课表Id和时段查询节次数量
        List<CourseScheduleDetail> courseScheduleDetailList = courseScheduleDetailMapper.findCourseScheduleDetailList(scheduleId,startTime,endTime);
        vo.setCourseDateList(courseDateVOList);
        vo.setNumber(ParamUtils.isNotEmpty(courseScheduleDetailList)?courseScheduleDetailList.size():0);
        return vo;
    }
}
